"""Работа с расходами - их добавление, удаление, статистика"""
import datetime
import re
from typing import List, NamedTuple, Optional

import pytz

import db
import exceptions
from categories import Categories


class Message(NamedTuple):
    """Стуктура распарсенного сообщения о новом расходе"""
    amount: int
    category_text: str


class Expense(NamedTuple):
    """ Структура добавленного в БД нового расхода"""
    id: Optional[int]
    amount: int
    category_name: str


def add_expense(raw_message: str) -> Expense:
    """Добавляет новое сообщение.
    Принимает на вход текст сообщения, пришедшего в бот."""
    parsed_message = _parse_message(raw_message)
    category = Categories().get_category(
            parsed_message.category_text)
    inserted_row_id = db.insert("expense", {
        "amount": parsed_message.amount,
        "created": _get_now_formatted(),
        "category_codename": category.codename,
        "raw_text": raw_message
    })
    
    if category.codename == '+':
        balance = float((db.fetchall("balance",["balance"]))[0]['balance']) + float(parsed_message.amount)
        update_balance = db.update_one_row("balance", {"balance": balance,})
    else:
       balance = float((db.fetchall("balance",["balance"]))[0]['balance']) - float(parsed_message.amount)
       update_balance = db.update_one_row("balance", {"balance": balance,})
    
    return Expense(id=None,
                    amount=parsed_message.amount,
                    category_name=category.name)


def get_today_statistics() -> str:
    """Возвращает строкой статистику расходов за сегодня"""
    cursor = db.get_cursor()
    cursor.execute("SELECT SUM(amount)"
                   "FROM expense WHERE DATE(created)=DATE('now', 'localtime')")
    result = cursor.fetchone()
    if not result[0]:
        return "Сегодня ещё нет расходов"
    all_today_expenses = result[0]
    cursor.execute("SELECT SUM(amount) "
                   "FROM expense WHERE DATE(created)=DATE('now', 'localtime') "
                   "AND category_codename IN (SELECT codename "
                   "FROM category WHERE is_base_expense=true)")
    result = cursor.fetchone()
    base_today_expenses = result[0] if result[0] else 0
    return (f"Расходы сегодня:\n"
            f"всего — {all_today_expenses} руб.\n"
            f"базовые — {base_today_expenses} руб. из {_get_budget_limit()} руб.\n\n"
            f"За текущий месяц: /month")


def get_month_statistics() -> str:
    """Возврат статистику за месяц"""
    now = _get_now_datetime()
    first_day_of_month = f'{now.year:04d}-{now.month:02d}-01'
    cursor = db.get_cursor()
    cursor.execute(f"SELECT SUM(amount) " 
                   f"FROM expense WHERE DATE(created) >= '{first_day_of_month}' ")
    result = cursor.fetchone()
    if not result[0]:
        return "В этом месяце не было расходов"
    all_today_expenses = result[0]
    cursor.execute(f"SELECT SUM(amount) "
                   f"FROM expense WHERE DATE(created) >= '{first_day_of_month}' "
                   f"AND category_codename IN (SELECT codename "
                   f"FROM category WHERE is_base_expense=true)")
    result = cursor.fetchone()
    base_today_expenses = result[0] if result[0] else 0
    return (f"Расходы в текущем месяце:\n"
            f"Всего - {all_today_expenses} руб.\n"
            f"Базовые - {base_today_expenses} руб. из "
            f"{now.day * _get_budget_limit()} руб.")


def last() -> List[Expense]:
    """Возвращает последние несколько расходов"""
    cursor = db.get_cursor()
    cursor.execute(
        "SELECT e.id, e.amount, c.name "
        "FROM expense e LEFT JOIN category c "
        "ON c.codename=e.category_codename "
        "ORDER BY CREATED DESC LIMIT 10")
    rows = cursor.fetchall()
    last_expenses = [Expense(id=row[0], amount=row[1], category_name=row[2]) \
            for row in rows]
    return last_expenses


def delete_expense(row_id: int) -> None:
    """Удаляет сообщение по его идентификатору"""
    # Перед удалением изменяем баланс на аналогичную сумму
    # Соответственно нам нужно запросить сумму которую удаляем по её id
    # Текущий баланс и сложить -> после записать эти данные в БД

    # Запрос удаляемой суммы
    cursor = db.get_cursor()
    cursor.execute(f"SELECT amount "
                   f"FROM expense WHERE id={row_id}")
    result = cursor.fetchone()
    db.delete("expense", row_id)
    balance = float((db.fetchall("balance",["balance"]))[0]['balance']) + float(result[0])
    update_balance = db.update_one_row("balance", {
         "balance": balance,
    })



def _parse_message(raw_message: str) -> Message:
    """Парсит текст полученного сообщения о новом расходе"""
    regexp_result = re.match(r"([\d ]+) (.*)", raw_message)
    if not regexp_result or not regexp_result.group(0) \
            or not regexp_result.group(1) or not regexp_result.group(2):
        raise exceptions.NotCorrectMessage( \
                "Не могу понять сообщение. Напишите сообщение в формате, "
                "например: \n100 такси")
    amount = regexp_result.group(1).replace(" ","")
    category_text = regexp_result.group(2).strip().lower()
    return Message(amount=amount, category_text=category_text)


def _get_now_formatted() -> str:
   """Возвращает сегодняшнюю дату строкой""" 
   return _get_now_datetime().strftime("%Y-%m-%d %H:%M:%S")


def _get_now_datetime() -> datetime.datetime:
    """Возвращает сегодняшний datetime с учётом временной зоны Мск."""
    tz = pytz.timezone("Europe/Moscow")
    now = datetime.datetime.now(tz)
    return now


def _get_budget_limit() -> int:
    """Возвращает дневной лимит трат для основных базовых трат"""
    return db.fetchall("budget",["daily_limit"])[0]["daily_limit"]


def get_balance() -> float:
    """Возвращает текущий баланс"""
    return db.fetchall("balance",["balance"])
